package com.sukaiyi.generator.utils;

import com.sukaiyi.generator.config.PluginConfig;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.compiled.ClsFieldImpl;
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.search.GlobalSearchScope;
import org.apache.commons.lang3.tuple.Pair;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author sukaiyi
 */
public class PsiClassUtils {

    /**
     * 获取 PsiClass 的某一类注解的属性值
     *
     * @param psiObject      要获取注解的对象（PsiClass, PsiFiled...）
     * @param annotationName 注解短名称
     * @param attrName       属性名称
     * @param def            获取到值时的默认返回值
     * @return PsiClass 的某一类注解的属性值
     */
    public static String getAnnotationValue(PsiModifierListOwner psiObject, String annotationName, String attrName, String def) {
        PsiAnnotation[] annotations = psiObject.getAnnotations();
        PsiAnnotation annotation = Arrays.stream(annotations)
                .filter(e -> e.getQualifiedName() != null)
                .filter(e -> e.getQualifiedName().endsWith(annotationName))
                .findFirst()
                .orElse(null);
        if (annotation == null) {
            return def;
        }
        PsiAnnotationMemberValue annotationMemberValue = annotation.findAttributeValue(attrName);
        if (annotationMemberValue == null) {
            return def;
        }
        if (annotationMemberValue instanceof PsiLiteralExpressionImpl) {
            String value = ((PsiLiteralExpressionImpl) annotationMemberValue).getInnerText();
            return value == null ? def : value;
        } else if (annotationMemberValue instanceof PsiReferenceExpressionImpl) {
            JavaResolveResult candidateInfo = ((PsiReferenceExpressionImpl) annotationMemberValue).advancedResolve(true);
            ClsFieldImpl element = (ClsFieldImpl) candidateInfo.getElement();
            if (element == null) {
                return def;
            }
            String value = element.getStub().getInitializerText();
            if (value.startsWith("\"") && value.endsWith("\"")) {
                return value.substring(1, value.length() - 1);
            }
            return value;
        }
        String value = annotationMemberValue.getText();
        if (value.startsWith("\"") && value.endsWith("\"")) {
            return value.substring(1, value.length() - 1);
        }
        return value;
    }

    /**
     * 为 PsiClass 添加注解
     *
     * @param psiObject  要添加注解的对象（PsiClass, PsiFiled...）
     * @param annotation 要添加的注解
     */
    public static void addAnnotation(PsiModifierListOwner psiObject, PsiAnnotation annotation) {
        if (psiObject.getModifierList() == null) {
            return;
        }
        psiObject.getModifierList().addAfter(annotation, null);
    }

    /**
     * 为 PsiClass 添加注解
     *
     * @param factory    生成注解的 factory
     * @param psiObject  要添加注解的对象（PsiClass, PsiFiled...）
     * @param annotation 要添加的注解
     */
    public static void addAnnotation(PsiElementFactory factory, PsiModifierListOwner psiObject, String annotation) {
        if (psiObject.getModifierList() == null) {
            return;
        }
        PsiAnnotation anno = factory.createAnnotationFromText(annotation, psiObject);
        addAnnotation(psiObject, anno);
    }

    /**
     * 为 PSI 对象添加 javadoc 注释
     *
     * @param factory   生成注释的 factory
     * @param psiObject 要添加注释的对象（PsiClass, PsiFiled...）
     * @param comment   要添加的注释
     */
    public static void addDocComment(PsiElementFactory factory, PsiJavaDocumentedElement psiObject, String comment) {
        if (comment == null || "".equals(comment.trim())) {
            return;
        }
        String rawComment = String.format("/**\n* %s\n*/", comment);
        PsiDocComment psiDocComment = factory.createDocCommentFromText(rawComment);
        psiObject.addAfter(psiDocComment, null);
    }

    /**
     * 为 PsiClass 添加实现的接口
     *
     * @param psiClass 要添加的类
     * @param interf   要实现的接口
     */
    public static void addImplements(PsiClass psiClass, PsiElement interf) {
        if (psiClass == null || psiClass.getImplementsList() == null) {
            return;
        }
        psiClass.getImplementsList().add(interf);
    }

    /**
     * 为 PsiClass 添加实现的接口
     *
     * @param psiClass 要添加的类
     * @param interf   要实现的接口
     */
    public static void addImplements(PsiClass psiClass, String interf) {
        if (psiClass == null || psiClass.getImplementsList() == null) {
            return;
        }
        Project project = psiClass.getProject();
        PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
        GlobalSearchScope scope = GlobalSearchScope.allScope(project);
        PsiJavaCodeReferenceElement rEle = factory.createReferenceElementByFQClassName(interf, scope);
        addImplements(psiClass, rEle);
    }

    /**
     * 获取 PsiClass 的子实体
     *
     * @param psiClass psiClass
     * @return psiClass 的 List 类型字段的泛类型，即子实体
     */
    public static Map<String, PsiClass> getSubEntities(PsiClass psiClass) {
        return Arrays.stream(psiClass.getFields())
                .filter(e -> e.getType().getPresentableText().startsWith("List"))
                .map(e -> {
                    PsiType psiType = e.getType();
                    String canonicalText = psiType.getCanonicalText();
                    String subEntityName = canonicalText.replaceAll(".*<|>", "");
                    Project project = psiClass.getContainingFile().getProject();
                    PsiClass clazz = JavaPsiFacade.getInstance(project).findClass(subEntityName, GlobalSearchScope.projectScope(project));
                    return Pair.of(e.getName(), clazz);
                })
                .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
    }

    /**
     * 获取类的完整包名（包含实体名）
     *
     * @param psiClass 要获取的类
     * @return 类的完整包名
     */
    public static String getFullName(PsiClass psiClass) {
        PsiJavaFile javaFile = (PsiJavaFile) psiClass.getContainingFile();
        return javaFile.getPackageName() + "." + psiClass.getName();
    }

    /**
     * 拷贝实体的字段给vo
     *
     * @param entity 实体
     * @param vo     vo
     */
    public static void copyEntityFieldsToVO(PsiClass entity, PsiClass vo) {

        List<String> dateClass = Arrays.asList("java.util.Date", "java.time.LocalDateTime");

        Project project = entity.getProject();
        PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
        PsiField[] fields = entity.getFields();
        for (PsiField field : fields) {
            if (field.getModifierList() != null &&
                    Arrays.stream(field.getModifierList().getChildren()).anyMatch(e -> e.getText().contains("static"))) {
                continue;
            }
            if (field.getName() == null) {
                continue;
            }
            PsiType voType = field.getType();
            if (voType.getCanonicalText().startsWith("java.util.List")) {
                String eleType = voType.getPresentableText().replaceAll(".*<|>", "");
                String eleVOType = eleType.replace(".java", "VO.java");
                voType = elementFactory.createTypeFromText("java.util.List<" + eleVOType + ">", vo);
            }
            if (voType.getCanonicalText().contains(".entity.")) {
                String eleVOType = voType.getCanonicalText().replace(".entity.", ".vo.") + "VO";
                voType = elementFactory.createTypeFromText(eleVOType, vo);
            }
            PsiField voField = elementFactory.createField(field.getName(), voType);
            if (PluginConfig.getInstance().voJsonFormat &&
                    dateClass.stream().anyMatch(e -> e.equals(field.getType().getCanonicalText()))
            ) {
                //  为vo的日期字段添加JsonFormat注解
                PsiClassUtils.addAnnotation(
                        elementFactory,
                        voField,
                        "@com.fasterxml.jackson.annotation.JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")");
            }
            vo.add(voField);
        }

    }

    /**
     * 拷贝实体的方法给vo
     *
     * @param entity 实体
     * @param vo     vo
     */
    public static void copyEntityMethodsToVO(PsiClass entity, PsiClass vo) {
        Project project = entity.getProject();
        PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
        PsiMethod[] subMethods = entity.getMethods();
        for (PsiMethod psiMethod : subMethods) {
            PsiType returnType = psiMethod.getReturnType();
            if (returnType == null) {
                continue;
            }
            if (returnType.getCanonicalText().contains(".entity.")) {
                returnType = elementFactory.createTypeByFQClassName(returnType.getPresentableText() + "VO");
            }
            PsiMethod method = elementFactory.createMethod(psiMethod.getName(), returnType);
            PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
            for (PsiParameter parameter : parameters) {
                if (parameter.getType().getCanonicalText().contains(".entity.")) {
                    PsiType pType = elementFactory.createTypeByFQClassName(parameter.getType().getPresentableText() + "VO");
                    parameter = elementFactory.createParameter(parameter.getName(), pType);
                }
                method.getParameterList().add(parameter);
            }

            PsiStatement[] statements = psiMethod.getBody().getStatements();
            for (PsiStatement statement : statements) {
                method.getBody().add(statement);
            }
            PsiAnnotation[] annotations = psiMethod.getAnnotations();
            for (PsiAnnotation annotation : annotations) {
                method.getModifierList().addAfter(annotation, null);
            }
            vo.add(method);
        }
    }

    /**
     * 格式化 class
     *
     * @param psiClass 要格式化的class
     */
    public static void format(PsiClass psiClass) {
        JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(psiClass.getProject());
        codeStyleManager.shortenClassReferences(psiClass);
        codeStyleManager.optimizeImports(psiClass.getContainingFile());
    }
}
